package com.bestcem.xm.member.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.common.core.constant.RegTypes;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.exception.GrpcCallFailedException;
import com.bestcem.xm.common.core.uitls.*;
import com.bestcem.xm.componet.filestorage.service.StorageService;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.label.dao.LabelDao;
import com.bestcem.xm.label.dao.LabelGroupDao;
import com.bestcem.xm.label.dao.LabelValueDao;
import com.bestcem.xm.label.entity.pojo.LabelDO;
import com.bestcem.xm.label.entity.pojo.LabelGroupDO;
import com.bestcem.xm.label.entity.pojo.LabelValueDO;
import com.bestcem.xm.label.grpc.service.param.label.ListOrgLabelValuesParam;
import com.bestcem.xm.label.service.dto.label.LabelDTO;
import com.bestcem.xm.label.service.dto.label.LabelValueDTO;
import com.bestcem.xm.member.constant.ColumnConstant;
import com.bestcem.xm.member.constant.Constants;
import com.bestcem.xm.member.constant.FieldDisplayNameConstant;
import com.bestcem.xm.member.dao.ExtendFieldDao;
import com.bestcem.xm.member.dao.ExtendFieldEnumItemDao;
import com.bestcem.xm.member.dao.MemberDao;
import com.bestcem.xm.member.dao.SourceCardDao;
import com.bestcem.xm.member.entity.mongo.ExtendFieldValue;
import com.bestcem.xm.member.entity.mysql.LabelValueId;
import com.bestcem.xm.member.entity.mysql.SourceCardDo;
import com.bestcem.xm.member.entity.pojo.ExtendFieldDO;
import com.bestcem.xm.member.entity.pojo.ExtendFieldEnumItemDO;
import com.bestcem.xm.member.entity.pojo.MemberDO;
import com.bestcem.xm.member.enums.*;
import com.bestcem.xm.member.grpc.client.XmMemberClient;
import com.bestcem.xm.member.grpc.service.param.PageParam;
import com.bestcem.xm.member.grpc.service.param.openplatform.*;
import com.bestcem.xm.member.mq.message.MemberMessage;
import com.bestcem.xm.member.service.GroupService;
import com.bestcem.xm.member.service.MemberOpenPlatformService;
import com.bestcem.xm.member.service.MemberService;
import com.bestcem.xm.member.service.SequenceService;
import com.bestcem.xm.member.service.dto.ExtendFieldDTO;
import com.bestcem.xm.member.service.dto.LabelValueIdDTO;
import com.bestcem.xm.member.service.dto.MemberDTO;
import com.bestcem.xm.member.service.dto.PageDTO;
import com.bestcem.xm.member.service.param.AddFootprintParam;
import com.bestcem.xm.member.util.CheckUtil;
import com.bestcem.xm.member.util.MemberUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.constraints.NotNull;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.bestcem.xm.member.constant.Constants.MEMBER_GENDER;
import static com.bestcem.xm.member.handler.member.BaseMemberHandle.INDIVIDUAL_MEMBER_NO_KEY;

/**
 * 开放平台
 *
 * @author yf.wang <yf.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/9/8
 */
@Slf4j
@Service
public class MemberOpenPlatformServiceImpl implements MemberOpenPlatformService {

    @Resource
    private XmMemberClient xmMemberClient;
    /**
     * 联系人dao
     */
    @Resource
    private MemberDao memberDao;

    @Resource
    private SequenceService sequenceService;

    @Resource
    private LabelGroupDao labelGroupDao;

    @Resource
    private LabelDao labelDao;

    @Resource
    private LabelValueDao labelValueDao;

    @Resource
    private ExtendFieldDao extendFieldDao;

    /**
     * 来源名片dao
     */
    @Resource
    private SourceCardDao sourceCardDao;

    @Resource
    private FootprintServiceImpl footprintService;

    @Resource
    private ExtendFieldEnumItemDao extendFieldEnumItemDao;

    @Resource
    private MemberUtil memberUtil;

    @Resource
    private RedisService redisService;

    /*@Resource
    private OssService ossService;*/
    @Autowired
    private StorageService storageService;

    @Resource
    private MemberService memberService;

    @Resource
    private GroupService groupService;

    @Override
    public ServiceResult<PageDTO<MemberDTO>> listMembers(ListOpenPlatformMembersParam param) {
        Integer offset = (param.getParam().getPage() - 1) * param.getParam().getSize();
        // 查询个人联系人数量
        Long count = memberDao.countByPeriod(param.getOrgId(), MemberTypeEnum.INDIVIDUAL.getIndex(), param.getStartCreateTime(), param.getEndCreateTime(), param.getStartUpdateTime(), param.getEndUpdateTime());
        if (param.getParam().getPage() < 1 || param.getParam().getSize() < 1) {
            return ServiceResult.fail("分页参数异常");
        }
        if (count <= offset || count < 1) {
            return ServiceResult.success(new PageDTO<>(new ArrayList<>(), count.intValue()));
        }

        // 查询联系人
        List<MemberDO> members = memberDao.listMemberByPeriod(param.getOrgId(),
                MemberTypeEnum.INDIVIDUAL.getIndex(), param.getStartCreateTime(),
                param.getEndCreateTime(), param.getStartUpdateTime(),
                param.getEndUpdateTime(),
                offset,
                param.getParam().getSize());
        if (CollectionUtils.isEmpty(members)) {
            log.warn("不存在对应的联系人：{}", param);
            return ServiceResult.success(new PageDTO<>(new ArrayList<>(), 0));
        }

        List<String> labelValueIds = new LinkedList<>();
        for (MemberDO member : members) {
            String memberLabels = member.getLabels();
            if (StrUtil.isNotBlank(memberLabels)) {
                List<LabelValueId> labels = JSONArray.parseArray(memberLabels, LabelValueId.class);
                if (CollUtil.isNotEmpty(labels)) {
                    for (LabelValueId label : labels) {
                        labelValueIds.add(label.getId());
                    }
                }
            }
        }

        // 查询标签值
        List<LabelValueDO> labelValues = labelValueDao.listByIds(labelValueIds);
        Map<String, LabelValueDO> labelValueIdMap = labelValues.stream().collect(Collectors.toMap(LabelValueDO::getLabelValueId, Function.identity()));
        List<MemberDTO> memberDTOList = new ArrayList<>(members.size());
        for (MemberDO member : members) {
            MemberDTO memberDTO = this.convert(member);
            // 填充标签值信息
            if (!CollectionUtils.isEmpty(memberDTO.getLabelValueIds())) {
                for (LabelValueIdDTO labelValueIdDTO : memberDTO.getLabelValueIds()) {
                    LabelValueDO labelValue = labelValueIdMap.get(labelValueIdDTO.getId());
                    if (labelValue == null) {
                        continue;
                    }
                    labelValueIdDTO.setLabelName(labelValue.getLabelName());
                    labelValueIdDTO.setValue(labelValue.getValue());
                }
            }
            memberDTOList.add(memberDTO);
        }
        return ServiceResult.success(new PageDTO<>(memberDTOList, count.intValue()));
    }

    @Override
    public ServiceResult<Map<UpsertMemberParam, String>> batchUpsertMembers(BatchUpsertMembersParam param) {
        List<UpsertMemberParam> upsertMemberParams = param.getUpsertMemberParams();

        Map<UpsertMemberParam, String> errorMsgMap;
        try {
            errorMsgMap = this.batchValidateUpsertMemberParams(param.getOrgId(), param.getUpsertMemberParams());
        } catch (GrpcCallFailedException e) {
            log.error("校验数据失败：{}", e.getDesc());
            return ServiceResult.fail(e.getDesc());
        }

        List<UpsertMemberParam> normalUpsertMembers = new ArrayList<>(upsertMemberParams.size());
        Date upsertTime = DateUtil.getCommonDate();
        for (UpsertMemberParam upsertMemberParam : upsertMemberParams) {
            upsertMemberParam.setOrgId(param.getOrgId());
            if (errorMsgMap.containsKey(upsertMemberParam)) {
                continue;
            }
            normalUpsertMembers.add(upsertMemberParam);
        }

        if (CollectionUtils.isEmpty(normalUpsertMembers)) {
            return ServiceResult.success(errorMsgMap);
        }

        // 保证dto与数据实体顺序一致
        Set<String> updateMemberIds = new HashSet<>();
        List<MemberDO> updateMembers = new ArrayList<>(normalUpsertMembers.size());
        for (UpsertMemberParam upsertMemberParam : normalUpsertMembers) {
            MemberDO member = this.convert(upsertMemberParam);
            member.setUpdateTime(upsertTime);
            // 计算年龄
            if (member.getBirthday() != null) {
                Integer age = DateUtil.calculateAge(member.getBirthday(), upsertTime);
                member.setAge(age);
            }
            // 新建联系人
            if (!StringUtils.hasText(member.getId())) {
                member.setCreateTime(upsertTime);
                // 所属组织
                member.setOrgId(param.getOrgId());
                // 序号
                ServiceResult<Integer> sequenceResult = sequenceService.getSequence(INDIVIDUAL_MEMBER_NO_KEY + param.getOrgId(), 1);
                if (!sequenceResult.isSuccess()) {
                    errorMsgMap.put(upsertMemberParam, "查询联系人序号失败");
                    continue;
                }
                member.setMemberNo("C" + NumberUtil.decimalFormat("00000000", sequenceResult.getData()));
                // 默认激活状态
                member.setStatus(MemberStatusEnum.ACTIVATED.getIndex());
                // 个人联系人
                member.setMemberType(MemberTypeEnum.INDIVIDUAL.getIndex());
            } else {
                updateMemberIds.add(member.getId());
            }
            updateMembers.add(member);
        }

        // 批量更新
        memberDao.batchUpsertOpenPlatformMembers(updateMembers);

        // 校验创建/更新结果
        for (int i = 0; i < updateMembers.size(); i++) {
            UpsertMemberParam upsertMemberParam = upsertMemberParams.get(i);
            MemberDO member = updateMembers.get(i);
            upsertMemberParam.setMemberId(member.getId());

            // 创建/更新联系人来源名片
            SourceCardDo card = new SourceCardDo();
            card.setId(StringUtil.getId());
            card.setOrgId(param.getOrgId());
            card.setSource(CardSourceTypeEnum.DATA.getIndex());
            card.setData(JSONObject.toJSONString(member));
            card.setMemberId(member.getId());

            // 创建/更新联系人消息
            MemberMessage message = new MemberMessage();
            BeanUtil.copyProperties(member, message);

            // 更新联系人
            if (updateMemberIds.contains(member.getId())) {
                // 推送更新联系人消息至BI
                message.setStatus(MemberStatusEnum.ACTIVATED.getIndex());
                memberUtil.publishUpdateMember(message);
            } else {
                // 创建来源名片
                card.setSerial(member.getMemberNo() + "-001");
                sourceCardDao.save(card);
                // 推送创建联系人消息至BI
                memberUtil.publishCreateMember(message);
                // 创建足迹点
                this.sendFootprintCreateMember(member);
            }
        }

        return ServiceResult.success(errorMsgMap);
    }

    @Override
    public ServiceResult<PageDTO<LabelDTO>> listLabelValues(ListOrgLabelValuesParam param) {
        // 校验分页信息
        PageParam page = param.getPageParam();
        if (page == null) {
            page = new PageParam(Constants.DEFAULT_PAGE, Constants.DEFAULT_LARGE_PAGE_SIZE);
        }
        if (page.getPage() <= 0 || page.getSize() <= 0) {
            return ServiceResult.fail("页数和单页长度必须大于0");
        }

        // 查询标签数量
        Integer offset = (page.getPage() - 1) * page.getSize();
        Long count = labelDao.countByOrgId(param.getOrgId());
        if (count <= offset) {
            return ServiceResult.success(new PageDTO<>(new ArrayList<>(), count.intValue()));
        }

        // 查询标签列表
        List<LabelDO> labels = labelDao.listByOrgId(param.getOrgId(), offset, page.getSize());
        if (CollectionUtils.isEmpty(labels)) {
            return ServiceResult.success(new PageDTO<>(new ArrayList<>(), count.intValue()));
        }

        Map<String, LabelDTO> idAndLabelMap = new HashMap<>();
        List<LabelDTO> labelDTOS = new ArrayList<>();
        List<String> labelIds = new ArrayList<>(labels.size());
        for (LabelDO label : labels) {
            labelIds.add(label.getLabelId());
            LabelDTO labelDTO = new LabelDTO();
            labelDTO.setId(label.getLabelId());
            labelDTO.setAuto(label.getAuto() == 1);
            labelDTO.setName(label.getName());
            labelDTO.setValues(new ArrayList<>());
            labelDTOS.add(labelDTO);
            idAndLabelMap.put(label.getLabelId(), labelDTO);
        }

        // 查询标签值列表
        List<LabelValueDO> labelValues = labelValueDao.findByLabelIds(param.getOrgId(), labelIds);
        for (LabelValueDO value : labelValues) {
            LabelDTO labelDTO = idAndLabelMap.get(value.getLabelId());
            labelDTO.setLabelGroupName(value.getLabelGroupName());
            LabelValueDTO valueDTO = this.convert(value);
            labelDTO.getValues().add(valueDTO);
        }
        return ServiceResult.success(new PageDTO<>(labelDTOS, count.intValue()));
    }

    private LabelValueDTO convert(LabelValueDO value) {
        LabelValueDTO res = new LabelValueDTO();
        res.setId(value.getLabelValueId());
        res.setValue(value.getValue());
        return res;
    }

    @Override
    public ServiceResult<Map<UpsertLabelParam, String>> batchUpsertLabels(BatchUpsertLabelsParam param) {
        // 简单校验
        Map<UpsertLabelParam, String> result = this.batchValidateParam(param);

        // 待新增标签分组
        Map<String, LabelGroupDO> insertGroupNameAndGroupMap = new HashMap<>();
        // 待新增/更新标签
        Map<String, List<LabelDO>> upsertGroupNameAndLabelMap = new HashMap<>();
        // 待删除标签值的标签id
        List<String> deleteValuesLabelIds = new ArrayList<>();
        // 待新增标签值
        List<LabelValueDO> allInsertValues = new ArrayList<>();

        // 企业下所有标签组
        List<LabelGroupDO> labelGroups = labelGroupDao.listByOrgId(param.getOrgId());
        Map<String, Integer> insertLabelCountMap = new HashMap<>();
        Map<String, LabelGroupDO> oldGroupNameMap = new HashMap<>();
        for (LabelGroupDO labelGroup : labelGroups) {
            oldGroupNameMap.put(labelGroup.getName(), labelGroup);
        }
        Set<String> labelGroupIds = new HashSet<>();
        List<String> labelIds = new ArrayList<>();
        List<String> labelNames = new ArrayList<>();
        for (UpsertLabelParam upsertLabelParam : param.getUpsertLabelParamList()) {
            if (result.containsKey(upsertLabelParam)) {
                continue;
            }
            if (StringUtils.hasText(upsertLabelParam.getLabelId())) {
                labelIds.add(upsertLabelParam.getLabelId());
            }
            if (StringUtils.hasText(upsertLabelParam.getLabelName())) {
                labelNames.add(upsertLabelParam.getLabelName());
            }
            LabelGroupDO labelGroup = oldGroupNameMap.get(upsertLabelParam.getLabelGroupName());
            if (labelGroup != null) {
                labelGroupIds.add(labelGroup.getId());
            }
        }
        // 相关标签
        List<LabelDO> labels = labelDao.listByOrgIdAndIds(param.getOrgId(), labelIds);
        List<LabelDO> nameLabels = labelDao.listByLabelNames(param.getOrgId(), labelNames);
        Map<String, LabelDO> labelIdAndLabelMap = labels.stream().collect(Collectors.toMap(LabelDO::getLabelId, Function.identity()));
        Map<String, LabelDO> oldLabelNameAndLabelMap = new HashMap<>(nameLabels.size());
        for (LabelDO label : nameLabels) {
            oldLabelNameAndLabelMap.put(label.getName(), label);
            labelGroupIds.add(label.getLabelGroupId());
        }
        labelGroupIds.addAll(labels.stream().map(LabelDO::getLabelGroupId).collect(Collectors.toList()));

        Map<String, Integer> labelGroupIdAndLabelCountMap = labelDao.countByLabelGroupIds(param.getOrgId(), labelGroupIds);

        // 扩展字段名
        List<ExtendFieldDO> extendFields = extendFieldDao.listMemberExtendField(param.getOrgId(), MemberTypeEnum.INDIVIDUAL.getIndex());
        Set<String> extendFieldSet = extendFields.stream().map(ExtendFieldDO::getName).collect(Collectors.toSet());

        Map<String, UpsertLabelParam> labelNameAndParamMap = new HashMap<>();
        Date currentTime = DateUtil.getCommonDate();
        // 查表校验
        for (UpsertLabelParam upsertLabelParam : param.getUpsertLabelParamList()) {
            if (result.containsKey(upsertLabelParam)) {
                continue;
            }
            // 校验与扩展字段名是否重名
            if (extendFieldSet.contains(upsertLabelParam.getLabelName())) {
                result.put(upsertLabelParam, "标签名与扩展字段名重复");
                continue;
            }
            // 标签名称是否重复
            LabelDO label = oldLabelNameAndLabelMap.get(upsertLabelParam.getLabelName());
            if (label != null && !label.getLabelId().equals(upsertLabelParam.getLabelId())) {
                result.put(upsertLabelParam, "该标签已存在");
                continue;
            }
            // 如果标签id存在，校验标签是否存在
            if (StringUtils.hasText(upsertLabelParam.getLabelId())) {
                label = labelIdAndLabelMap.get(upsertLabelParam.getLabelId());
                if (label == null) {
                    result.put(upsertLabelParam, "该标签不存在");
                    continue;
                }
            }
            // 标签组是否存在
            LabelGroupDO labelGroup = oldGroupNameMap.get(upsertLabelParam.getLabelGroupName());
            if (labelGroup == null) {
                if (insertGroupNameAndGroupMap.size() + labelGroups.size() >= Constants.MAX_LABEL_GROUP_SIZE) {
                    result.put(upsertLabelParam, String.format("标签分组数量不能超过%s个", Constants.MAX_LABEL_GROUP_SIZE));
                    continue;
                }
                labelGroup = new LabelGroupDO();
                labelGroup.setOrgId(param.getOrgId());
                labelGroup.setName(upsertLabelParam.getLabelGroupName());
                labelGroup.setMemberType(MemberTypeEnum.INDIVIDUAL.getIndex());
                labelGroup.setSeq(labelGroups.size() + 1);
                labelGroup.setIsSample(0);
                labelGroup.setCreateTime(currentTime);
                labelGroup.setUpdateTime(currentTime);
            }
            if (!insertLabelCountMap.containsKey(labelGroup.getName())) {
                insertLabelCountMap.put(labelGroup.getName(), 0);
            }

            // 插入情况下校验标签数量
            Integer oldGroupLabelCount = Optional.ofNullable(labelGroupIdAndLabelCountMap.get(labelGroup.getId())).orElse(0);
            Integer newGroupLabelCount = insertLabelCountMap.get(labelGroup.getName());
            if (label == null || !label.getLabelGroupId().equals(labelGroup.getId())) {
                if (oldGroupLabelCount + newGroupLabelCount >= Constants.MAX_LABEL_SIZE) {
                    result.put(upsertLabelParam, String.format("一个标签组下不能超过%s个标签", Constants.MAX_LABEL_SIZE));
                    continue;
                }
            }

            List<LabelDO> upsertGroupSubLabels = upsertGroupNameAndLabelMap.computeIfAbsent(labelGroup.getName(), k -> new ArrayList<>());
            // 如果是待更新标签
            if (label != null) {
                if (label.getAuto().equals(1)) {
                    result.put(upsertLabelParam, "系统自动标记的标签无法更新");
                    continue;
                }
                // 更新所属分组
                if (!label.getLabelGroupId().equals(labelGroup.getId())
                        || !upsertLabelParam.getLabelName().equals(label.getName())) {
                    label.setName(upsertLabelParam.getLabelName());
                    label.setUpdateTime(currentTime);
                    upsertGroupSubLabels.add(label);
                    // 新标签数量+1
                    insertLabelCountMap.put(labelGroup.getName(), newGroupLabelCount + 1);
                    if (!label.getLabelGroupId().equals(labelGroup.getId())) {
                        label.setLabelGroupId(labelGroup.getId());
                        // 老标签组标签数-1
                        labelGroupIdAndLabelCountMap.put(labelGroup.getId(), oldGroupLabelCount - 1);
                    }
                }
                // 待删除标签下标签值
                deleteValuesLabelIds.add(label.getLabelId());
            } else {
                label = new LabelDO();
                label.setOrgId(param.getOrgId());
                label.setName(upsertLabelParam.getLabelName());
                label.setAuto(0);
                label.setManual(1);
                label.setIsSample(0);
                label.setCreateTime(currentTime);
                label.setUpdateTime(currentTime);
                upsertGroupSubLabels.add(label);
                // 新标签数量+1
                insertLabelCountMap.put(labelGroup.getName(), newGroupLabelCount + 1);
            }

            // 待创建标签值
            for (String value : upsertLabelParam.getLabelValueNames()) {
                if (value.length() > Constants.MAX_LABEL_VALUE_NAME_LENGTH) {
                    value = value.substring(0, Constants.MAX_LABEL_VALUE_NAME_LENGTH);
                }
                LabelValueDO labelValue = new LabelValueDO();
                labelValue.setOrgId(param.getOrgId());
                labelValue.setLabelId(label.getLabelId());
                labelValue.setSeq(0);
                labelValue.setAuto(0);
                labelValue.setManual(1);
                labelValue.setMemberType(MemberTypeEnum.INDIVIDUAL.getIndex());
                labelValue.setLabelName(upsertLabelParam.getLabelName());
                labelValue.setLabelGroupName(upsertLabelParam.getLabelGroupName());
                labelValue.setValue(value);
                labelValue.setIsSample(0);
                labelValue.setCreateTime(currentTime);
                labelValue.setUpdateTime(currentTime);
                allInsertValues.add(labelValue);
            }
            labelNameAndParamMap.put(label.getName(), upsertLabelParam);

            // 待创建标签组
            if (!StringUtils.hasText(labelGroup.getId())) {
                insertGroupNameAndGroupMap.put(labelGroup.getName(), labelGroup);
            }
        }

        // 删除标签值
        if (!CollectionUtils.isEmpty(deleteValuesLabelIds)) {
            // memberService.deleteMemberLabelByLabelIds(param.getOrgId(), MemberTypeEnum.INDIVIDUAL.getIndex(), deleteValuesLabelIds);
            labelValueDao.batchDeleteByLabelIds(param.getOrgId(), MemberTypeEnum.INDIVIDUAL.getIndex(), deleteValuesLabelIds);
        }
        // 新增标签分组
        Collection<LabelGroupDO> insertedLabelGroups = labelGroupDao.batchSave(insertGroupNameAndGroupMap.values());

        // 关联标签组，新增标签
        Map<String, LabelGroupDO> insertedGroupNameMap = insertedLabelGroups.stream()
                .collect(Collectors.toMap(LabelGroupDO::getName, Function.identity()));
        oldGroupNameMap.putAll(insertedGroupNameMap);
        Collection<LabelDO> insertedLabels = new ArrayList<>();
        Collection<LabelDO> updatedLabels = new ArrayList<>();
        for (Map.Entry<String, List<LabelDO>> entry : upsertGroupNameAndLabelMap.entrySet()) {
            LabelGroupDO labelGroup = oldGroupNameMap.get(entry.getKey());
            for (LabelDO label : entry.getValue()) {
                if (!StringUtils.hasText(label.getLabelId())) {
                    insertedLabels.add(label);
                } else {
                    updatedLabels.add(label);
                }
                if (!labelGroup.getId().equals(label.getLabelGroupId())) {
                    label.setLabelGroupId(labelGroup.getId());
                }
            }
        }
        insertedLabels = labelDao.batchSave(insertedLabels);
        // 更新标签所属标签组、名称
        labelDao.batchUpdate(updatedLabels);

        // 关联标签，新增标签值
        oldLabelNameAndLabelMap.putAll(updatedLabels.stream().collect(Collectors.toMap(LabelDO::getName, Function.identity())));
        for (LabelDO label : insertedLabels) {
            oldLabelNameAndLabelMap.put(label.getName(), label);
            labelNameAndParamMap.get(label.getName()).setLabelId(label.getLabelId());
        }
        for (LabelValueDO value : allInsertValues) {
            LabelGroupDO labelGroup = oldGroupNameMap.get(value.getLabelGroupName());
            LabelDO label = oldLabelNameAndLabelMap.get(value.getLabelName());
            value.setLabelGroupId(labelGroup.getId());
            value.setLabelId(label.getLabelId());
        }
        if (!CollectionUtils.isEmpty(allInsertValues)) {
            labelValueDao.batchSave(allInsertValues);
        }

        // 异步更新联系人分组信息
        groupService.sendGroupMessages(param.getOrgId());

        return ServiceResult.success(result);
    }

    /**
     * 推送创建的联系人信息至足迹
     *
     * @param createdMember 已创建的联系人
     */
    private void sendFootprintCreateMember(MemberDO createdMember) {
        JSONObject footprint = new JSONObject();
        footprint.put("title", "成为联系人");
        AddFootprintParam addFootprintParam = new AddFootprintParam();
        addFootprintParam.setOrgId(createdMember.getOrgId());
        addFootprintParam.setMemberId(createdMember.getId());
        addFootprintParam.setType(FootprintTypeEnum.MEMBER.getIndex());
        addFootprintParam.setSourceKey(createdMember.getId());
        addFootprintParam.setContent(footprint);
        addFootprintParam.setFootprintTime(createdMember.getCreateTime());
        log.info("发送创建足迹点：{}", addFootprintParam);
        footprintService.publishFootprint(addFootprintParam);
    }

    private Map<UpsertMemberParam, String> batchValidateUpsertMemberParams(String orgId, List<UpsertMemberParam> params) throws GrpcCallFailedException {
        Map<UpsertMemberParam, String> errorMsgMap = new HashMap<>();

        // 联系人信息
        List<String> oldMemberIds = new ArrayList<>();
        Set<String> phones = new HashSet<>();
        Set<String> emails = new HashSet<>();
        Set<String> outNos = new HashSet<>();
        Set<String> appIdAndOpenIds = new HashSet<>();

        // 查询地址缓存
        //String filePath = ossService.getFilePrefix() + Constants.CITY_FIELD_PATH;
        String filePath = storageService.getPropertiesValueBy(null, "base-path") + Constants.CITY_FIELD_PATH;
        //Map<String, Map> addressMap = this.flashAddressMap(filePath);
        JSONObject addressJsonObject = flashAddressJsonObject(filePath);


        // 获取当前时间
        Date upsertTime = DateUtil.getCommonDate();
        for (UpsertMemberParam upsertMemberParam : params) {
            // 参数校验
            upsertMemberParam.setOrgId(orgId);
            Set<ConstraintViolation<Object>> violations = GRpcUtil.validateParam(upsertMemberParam);
            if (!CollectionUtils.isEmpty(violations)) {
                ConstraintViolation<Object> violation = violations.iterator().next();
                errorMsgMap.put(upsertMemberParam, violation.getMessage());
                continue;
            }
            // 校验待更新联系人id
            if (StringUtils.hasText(upsertMemberParam.getMemberId())) {
                if (!Pattern.matches(RegTypes.OBJECT_ID, upsertMemberParam.getMemberId())) {
                    errorMsgMap.put(upsertMemberParam, String.format("联系人id:%s异常", upsertMemberParam.getMemberId()));
                    continue;
                }
                oldMemberIds.add(upsertMemberParam.getMemberId());
            }
            // 校验组织id格式
            if (StringUtils.hasText(upsertMemberParam.getStore())) {
                if (!Pattern.matches(RegTypes.OBJECT_ID, upsertMemberParam.getStore())) {
                    errorMsgMap.put(upsertMemberParam, String.format("组织id:%s异常", upsertMemberParam.getStore()));
                    continue;
                }
            }
            // 校验地址
            //String errorMsg = this.validateAddress(upsertMemberParam.getAddress(), addressMap);
            String errorMsg = this.validateAddress(upsertMemberParam.getAddress(), addressJsonObject);
            if (StringUtils.hasText(errorMsg)) {
                errorMsgMap.put(upsertMemberParam, errorMsg);
                continue;
            }
            // 校验生日
            if (upsertMemberParam.getBirthday() != null && upsertTime.before(upsertMemberParam.getBirthday())) {
                errorMsgMap.put(upsertMemberParam, "联系人生日不能大于当前时间");
                continue;
            }
            // 校验手机号、邮箱
            if (!StringUtils.hasText(upsertMemberParam.getPhone()) && !StringUtils.hasText(upsertMemberParam.getEmail())
                    && !StringUtils.hasText(upsertMemberParam.getOutNo())) {
                // AppID和OpenID必须同时存在
                if (StrUtil.isEmpty(upsertMemberParam.getAppId()) || StrUtil.isEmpty(upsertMemberParam.getOpenId())) {
                    errorMsgMap.put(upsertMemberParam, "手机号、邮箱、外部编号、微信appID+openID至少填写一个");
                    continue;
                }
            }
            if (StrUtil.isEmpty(upsertMemberParam.getAppId()) ^ StrUtil.isEmpty(upsertMemberParam.getOpenId())) {
                String errorMessage = "微信appID+openID必须同时存在";
                errorMsgMap.put(upsertMemberParam, errorMessage);
                continue;
            }
            if (StringUtils.hasText(upsertMemberParam.getPhone())) {
                if (phones.contains(upsertMemberParam.getPhone())) {
                    errorMsgMap.put(upsertMemberParam, "存在重复手机号");
                    continue;
                }
                phones.add(upsertMemberParam.getPhone());
            }
            if (StringUtils.hasText(upsertMemberParam.getEmail())) {
                if (emails.contains(upsertMemberParam.getEmail())) {
                    errorMsgMap.put(upsertMemberParam, "存在重复邮箱");
                    continue;
                }
                emails.add(upsertMemberParam.getEmail());
            }
            if (StringUtils.hasText(upsertMemberParam.getOutNo())) {
                if (outNos.contains(upsertMemberParam.getOutNo())) {
                    errorMsgMap.put(upsertMemberParam, "存在重复外部编号");
                    continue;
                }
                outNos.add(upsertMemberParam.getOutNo());
            }
            if (StringUtils.hasText(upsertMemberParam.getAppId()) && StringUtils.hasText(upsertMemberParam.getOpenId())) {
                if (!appIdAndOpenIds.add(upsertMemberParam.getAppId() + upsertMemberParam.getOpenId())) {
                    errorMsgMap.put(upsertMemberParam, "存在重复微信appID+openID");
                    continue;
                }
                if (!CheckUtil.checkAppIdFormat(upsertMemberParam.getAppId(), MemberTypeEnum.INDIVIDUAL.getIndex()).isSuccess()) {
                    errorMsgMap.put(upsertMemberParam, "联系人AppId格式异常");
                    continue;
                }
                if (!CheckUtil.checkOpenIdFormat(upsertMemberParam.getOpenId(), MemberTypeEnum.INDIVIDUAL.getIndex()).isSuccess()) {
                    errorMsgMap.put(upsertMemberParam, "联系人OpenId格式异常");
                    continue;
                }
                MemberDO appIdAndOpenIdMember = memberDao.findByAppIdAndOpenId(orgId, MemberTypeEnum.INDIVIDUAL.getIndex(),
                        upsertMemberParam.getAppId(), upsertMemberParam.getOpenId());
                if (appIdAndOpenIdMember != null && !appIdAndOpenIdMember.getId().equals(upsertMemberParam.getMemberId())) {
                    errorMsgMap.put(upsertMemberParam, "联系人微信appID+openID已存在");
                    continue;
                }
            }
            if (StringUtils.hasText(upsertMemberParam.getAvatar())
                    && !CheckUtil.checkAvatarFormat(upsertMemberParam.getAvatar(), MemberTypeEnum.INDIVIDUAL.getIndex()).isSuccess()) {
                errorMsgMap.put(upsertMemberParam, "联系人Avatar格式异常");
                continue;
            }
            if (StringUtils.hasText(upsertMemberParam.getUnionId())
                    && !CheckUtil.checkUnionIdFormat(upsertMemberParam.getUnionId(), MemberTypeEnum.INDIVIDUAL.getIndex()).isSuccess()) {
                errorMsgMap.put(upsertMemberParam, "联系人UnionId格式异常");
                continue;
            }
            if (StringUtils.hasText(upsertMemberParam.getNickname())
                    && !CheckUtil.checkNicknameFormat(upsertMemberParam.getNickname(), MemberTypeEnum.INDIVIDUAL.getIndex()).isSuccess()) {
                errorMsgMap.put(upsertMemberParam, "联系人Nickname格式异常");
                continue;
            }
            if (StringUtils.hasText(upsertMemberParam.getPhone()) && !CheckUtil.checkMobilePhoneFormat(upsertMemberParam.getPhone()).isSuccess()) {
                errorMsgMap.put(upsertMemberParam, "联系人手机号格式异常");
                continue;
            }
            if (StringUtils.hasText(upsertMemberParam.getEmail()) && !CheckUtil.checkEmailFormat(upsertMemberParam.getEmail()).isSuccess()) {
                errorMsgMap.put(upsertMemberParam, "联系人邮箱格式异常");
                continue;
            }
            if (StringUtils.hasText(upsertMemberParam.getOutNo())) {
                if (!CheckUtil.checkOutNoFormat(upsertMemberParam.getOutNo(), MemberTypeEnum.INDIVIDUAL.getIndex()).isSuccess()) {
                    errorMsgMap.put(upsertMemberParam, "联系人外部编号格式异常");
                    continue;
                }
                MemberDO outNoMember = memberDao.findByOrgIdAndFieldAndAttributeAndStatus(orgId, ColumnConstant.Member.OUT_NO,
                        upsertMemberParam.getOutNo(), MemberTypeEnum.INDIVIDUAL.getIndex(), MemberStatusEnum.ACTIVATED.getIndex());
                if (outNoMember != null && !outNoMember.getId().equals(upsertMemberParam.getMemberId())) {
                    errorMsgMap.put(upsertMemberParam, "联系人外部编号已存在");
                    continue;
                }
            }
            // 性别校验 必须是 男 女
            if (StringUtils.hasText(upsertMemberParam.getGender()) && !MEMBER_GENDER.contains(upsertMemberParam.getGender())) {
                errorMsgMap.put(upsertMemberParam, "性别必须是: 男 女");
            }
        }

        // 查询组织信息
        ServiceResult<JSONObject> storeResult = xmMemberClient.getStoreIdNameMap(orgId);
        if (!storeResult.isSuccess()) {
            throw new GrpcCallFailedException(String.format("查询组织（%s）信息失败：%s", orgId, storeResult.getMsg()));
        }
        JSONObject storeIdAndNameMap = storeResult.getData();

        // 查询待更新联系人
        List<MemberDO> oldMembers = memberDao.list(orgId, oldMemberIds);

        Map<String, MemberDO> idMap = new HashMap<>();
        Map<String, MemberDO> phoneMap = new HashMap<>();
        Map<String, MemberDO> emailMap = new HashMap<>();
        for (MemberDO member : oldMembers) {
            idMap.put(member.getId(), member);
            if (StringUtils.hasText(member.getPhone())) {
                phoneMap.put(member.getPhone(), member);
            }
            if (StringUtils.hasText(member.getEmail())) {
                emailMap.put(member.getEmail(), member);
            }
        }

        // 通过手机号、邮箱查询联系人
        List<MemberDO> contactMembers = new LinkedList<>();
        if (!CollectionUtils.isEmpty(phones)) {
            contactMembers.addAll(memberDao.listMembersByPhones(orgId, MemberTypeEnum.INDIVIDUAL.getIndex(), phones));
        }
        if (!CollectionUtils.isEmpty(emails)) {
            contactMembers.addAll(memberDao.listMembersByEmails(orgId, MemberTypeEnum.INDIVIDUAL.getIndex(), emails));
        }
        for (MemberDO member : contactMembers) {
            idMap.put(member.getId(), member);
            if (StringUtils.hasText(member.getPhone())) {
                phoneMap.put(member.getPhone(), member);
            }
            if (StringUtils.hasText(member.getEmail())) {
                emailMap.put(member.getEmail(), member);
            }
        }

        // 查询扩展字段
        List<ExtendFieldDO> extendFields = extendFieldDao.listMemberExtendField(orgId, MemberTypeEnum.INDIVIDUAL.getIndex());
        Collection<ExtendFieldEnumItemDO> enumItems = extendFieldEnumItemDao.findByFieldIds(extendFields.stream().map(ExtendFieldDO::getFieldId).collect(Collectors.toList()));
        Map<String, ExtendFieldDO> fieldNameMap = extendFields.stream().collect(Collectors.toMap(ExtendFieldDO::getName, Function.identity()));
        Map<String, ExtendFieldEnumItemDO> fieldIdAndEnumNameMap = enumItems.stream().collect(Collectors.toMap(e -> e.getExtendFieldId() + e.getName(), Function.identity()));

        // 查询标签
        List<LabelValueDO> labelValues = labelValueDao.listByOrgIdAndMemberType(orgId, MemberTypeEnum.INDIVIDUAL.getIndex());
        Map<String, LabelValueDO> nameAndValueMap = new HashMap<>(labelValues.size());
        for (LabelValueDO labelValue : labelValues) {
            nameAndValueMap.put(labelValue.getLabelName() + labelValue.getValue(), labelValue);
        }

        // 查询企业联系人总数
        Long currentMemberAmount = memberDao.countIndividualMember(orgId);

        // 查询联系人限制数量
        ServiceResult<Integer> userPackageQuoteResult = xmMemberClient.getUserPackageQuota(orgId);
        if (!userPackageQuoteResult.isSuccess()) {
            log.error("查询用户限额失败: {}", userPackageQuoteResult);
            throw new GrpcCallFailedException(String.format("查询组织（%s）限额信息失败：%s", orgId, userPackageQuoteResult.getMsg()));
        }
        Integer maxMemberAmount = userPackageQuoteResult.getData();

        for (UpsertMemberParam upsertMemberParam : params) {
            upsertMemberParam.setOrgId(orgId);
            // 校验待更新联系人
            if (StringUtils.hasText(upsertMemberParam.getMemberId())) {
                // 校验待更新联系人是否存在
                if (!idMap.containsKey(upsertMemberParam.getMemberId())) {
                    errorMsgMap.put(upsertMemberParam, String.format("联系人:%s不存在", upsertMemberParam.getMemberId()));
                    continue;
                }
            }
            // 校验联系人手机号、邮箱是否重复、这里没有排除同批次修改的联系人
            MemberDO contactMember = phoneMap.containsKey(upsertMemberParam.getPhone()) ? phoneMap.get(upsertMemberParam.getPhone()) : emailMap.get(upsertMemberParam.getEmail());
            if (contactMember != null && !contactMember.getId().equals(upsertMemberParam.getMemberId())) {
                errorMsgMap.put(upsertMemberParam, "手机号或邮箱重复");
                continue;
            }
            // 校验组织
            if (StringUtils.hasText(upsertMemberParam.getStore()) && !storeIdAndNameMap.containsKey(upsertMemberParam.getStore())) {
                errorMsgMap.put(upsertMemberParam, String.format("组织id:%s不存在", upsertMemberParam.getStore()));
                continue;
            }
            // 校验标签
            String errorMsg = this.validateMemberLabels(upsertMemberParam.getLabels(), nameAndValueMap);
            if (StringUtils.hasText(errorMsg)) {
                errorMsgMap.put(upsertMemberParam, errorMsg);
                continue;
            }

            // 校验扩展字段、添加冗余信息
            if (upsertMemberParam.getFields() == null) {
                upsertMemberParam.setFields(new ArrayList<>());
            }
            errorMsg = this.validateMemberFields(upsertMemberParam.getFields(), fieldNameMap, fieldIdAndEnumNameMap);
            if (StringUtils.hasText(errorMsg)) {
                errorMsgMap.put(upsertMemberParam, errorMsg);
                continue;
            }

            // 校验联系人数量 -1代表无限额
            if (!Constants.NEGATIVE.equals(maxMemberAmount)
                    && !StringUtils.hasText(upsertMemberParam.getMemberId())
                    && ++currentMemberAmount > maxMemberAmount) {
                errorMsgMap.put(upsertMemberParam, "创建联系人数量超过最大限制：" + maxMemberAmount);
            }
        }
        return errorMsgMap;
    }

    private String validateAddress(List<String> address, JSONObject addressMap) {
        if (CollectionUtils.isEmpty(address)) {
            return "";
        }

        // 层级不固定,最大只校验到第3层级
        int index = 0;
        if (address.size() > 4) {
            return "地址层级不能大于4";
        }
        for (String s : address) {
            if (++index >= 5) {
                break;
            }
            if (!StringUtils.hasText(s)) {
                return "地址为空";
            }
            if (CollectionUtils.isEmpty(addressMap)) {
                return "地址不存在";
            }
            if (!addressMap.containsKey(s)) {
                return String.format("第%s级地址范围应包含于：%s", index, addressMap.keySet());
            }
            addressMap = addressMap.getJSONObject(s);
        }
        return "";
    }

    //private String validateAddress(List<String> address, Map<String, Map> addressMap) {
    //    if (CollectionUtils.isEmpty(address)) {
    //        return "";
    //    }
    //
    //    // 层级不固定,最大只校验到第3层级
    //    int index = 0;
    //    if (address.size() > 4) {
    //        return "地址层级不能大于4";
    //    }
    //    for (String s : address) {
    //        if (++index >= 4) {
    //            break;
    //        }
    //        if (!StringUtils.hasText(s)) {
    //            return "地址为空";
    //        }
    //        if (CollectionUtils.isEmpty(addressMap)) {
    //            return "地址不存在";
    //        }
    //        if (!addressMap.containsKey(s)) {
    //            return String.format("第%s级地址范围应包含于：%s", index, addressMap.keySet());
    //        }
    //        addressMap = addressMap.get(s);
    //    }
    //    return "";
    //}

    /*public Map<String, Map> flashAddressMap(String cityFilePath) {
        Map<String, Map> cityCache = redisService.hashGetAll(Constants.CITY_LEVEL_KEY);
        if (CollectionUtils.isEmpty(cityCache)) {
            // 下载投放上传的联系人excel文件
            try {
                InputStream is = ossService.downloadFile(cityFilePath);
                byte[] bytes = InputStreamUtil.readNBytes(is, Integer.MAX_VALUE);
                String s = new String(bytes);
                cityCache = CommonUtil.formatAddressMap(JSONArray.parseArray(s));
            } catch (IOException e) {
                log.error("读取文件异常：{}", e.getMessage());
                return new HashMap<>();
            } catch (Exception e) {
                log.error("刷新地址缓存异常：{}", e.getMessage());
                return new HashMap<>();
            }
            redisService.hashSetAll(Constants.CITY_LEVEL_KEY, cityCache);
            redisService.expire(Constants.CITY_LEVEL_KEY, 1L, TimeUnit.DAYS);
        }
        return cityCache;
    }*/

    public JSONObject flashAddressJsonObject(String cityFilePath) {
        JSONObject cityCache = (JSONObject) redisService.get(Constants.CITY_LEVEL_KEY);
        if (CollectionUtils.isEmpty(cityCache)) {
            // 下载投放上传的联系人excel文件
            try {
                //InputStream is = ossService.downloadFile(cityFilePath);
                //InputStream is = storageService.downloadFile(cityFilePath);
                InputStream is = new ByteArrayInputStream(storageService.downloadByUrl(storageService.getPropertiesValueBy(null, "domain") + cityFilePath).bytes());

                byte[] bytes = InputStreamUtil.readNBytes(is, Integer.MAX_VALUE);
                String s = new String(bytes);
                cityCache = CommonUtil.formatAddressJsonObject(JSONArray.parseArray(s));
            } catch (Exception e) {
                log.error("读取文件异常：{}", e.getMessage());
                return new JSONObject();
            }
            redisService.set(Constants.CITY_LEVEL_KEY, cityCache);
            redisService.expire(Constants.CITY_LEVEL_KEY, 1L, TimeUnit.DAYS);
        }
        return cityCache;
    }

    /**
     * 校验扩展字段标签
     *
     * @param labelParams     标签参数
     * @param nameAndValueMap 标签名、标签值Map
     * @return 错误信息
     */
    private String validateMemberLabels(List<MemberLabelParam> labelParams, Map<String, LabelValueDO> nameAndValueMap) {
        if (CollectionUtils.isEmpty(labelParams)) {
            return "";
        }

        for (MemberLabelParam memberLabelParam : labelParams) {
            LabelValueDO labelValue = nameAndValueMap.get(memberLabelParam.getLabelName() + memberLabelParam.getValue());
            if (labelValue == null) {
                return "标签不存在";
            }
            // 校验是否允许手动操作
            if (labelValue.getManual() == null || !labelValue.getManual().equals(1)) {
                return String.format("存在不允许手动修改的标签：%s", labelValue.getLabelName());
            }
            memberLabelParam.setLabelId(labelValue.getLabelId());
            memberLabelParam.setId(labelValue.getLabelValueId());
        }
        return "";
    }

    private String validateMemberFields(@NotNull List<MemberFieldParam> fieldParams, Map<String, ExtendFieldDO> fieldNameMap, Map<String, ExtendFieldEnumItemDO> fieldIdAndEnumNameMap) {
        Set<String> existsFieldIds = new HashSet<>();
        for (int i = 0; i < fieldParams.size(); i++) {
            MemberFieldParam fieldParam = fieldParams.get(i);
            ExtendFieldDO extendField = fieldNameMap.get(fieldParam.getName());
            if (extendField == null) {
                return String.format("第%s行扩展字段不存在", i + 1);
            }
            if (existsFieldIds.contains(extendField.getFieldId())) {
                return String.format("第%s行扩展字段重复", i + 1);
            }

            ExtendFieldTypeEnum fieldTypeEnum = EnumUtil.getEnumByIndex(ExtendFieldTypeEnum.class, extendField.getType());
            if (fieldTypeEnum == null) {
                return String.format("第%s行扩展字段数据异常", i + 1);
            }

            ServiceResult<String> result = null;
            switch (fieldTypeEnum) {
                case NUMBER:
                    result = CheckUtil.checkNumberFormat(fieldParam.getValue());
                    break;
                case DATETIME:
                    result = CheckUtil.checkVariousDateFormat(fieldParam.getValue());
                    break;
                case EMAIL:
                    result = CheckUtil.checkEmailFormat(fieldParam.getValue());
                    break;
                case ENUM:
                    if (StringUtils.hasText(fieldParam.getValue()) && !fieldIdAndEnumNameMap.containsKey(extendField.getFieldId() + fieldParam.getValue())) {
                        result = ServiceResult.fail(String.format("枚举选项:%s不存在", fieldParam.getValue()));
                    }
                    break;
                case MOBILE:
                    result = CheckUtil.checkMobilePhoneFormat(fieldParam.getValue());
            }

            if (result != null && !result.isSuccess()) {
                return String.format("第%s行扩展字段%s", i + 1, result.getMsg());
            }
            fieldParam.setType(extendField.getType());
            fieldParam.setId(extendField.getFieldId());
            existsFieldIds.add(extendField.getFieldId());
        }

        // 补充冗余字段
        for (ExtendFieldDO extendField : fieldNameMap.values()) {
            if (existsFieldIds.contains(extendField.getFieldId())) {
                continue;
            }
            MemberFieldParam fieldParam = new MemberFieldParam();
            fieldParam.setId(extendField.getFieldId());
            fieldParam.setType(extendField.getType());
            fieldParam.setName(extendField.getName());
            fieldParams.add(fieldParam);
        }

        return "";
    }

    private MemberDO convert(UpsertMemberParam dto) {
        MemberDO res = new MemberDO();
        res.setId(dto.getMemberId());
        res.setOrgId(dto.getOrgId());
        res.setSysId(dto.getSysId());
        res.setSource(dto.getSource());
        res.setName(dto.getName());
        res.setGender(dto.getGender());
        if (CollUtil.isNotEmpty(dto.getAddress())) {
            res.setAddress(JSONObject.toJSONString(dto.getAddress()));
        }
        res.setPhone(dto.getPhone());
        res.setEmail(dto.getEmail());
        res.setBirthday(dto.getBirthday());
        res.setAge(dto.getAge());
        res.setStoreId(dto.getStore());
        res.setStatus(dto.getStatus());
        res.setContent(dto.getContent());
        res.setEmployeeId(dto.getEmployeeId());
        res.setEmployeeStatus(dto.getEmployeeStatus());
        res.setDepartment(dto.getDepartment());
        res.setPosition(dto.getPosition());
        res.setInstituteId(dto.getInstituteId());
        res.setInstituteName(dto.getInstituteName());
        res.setUserId(dto.getUserId());
        res.setMemberNo(dto.getMemberNo());
        res.setMemberType(dto.getMemberType());
        res.setOpenId(dto.getOpenId());
        res.setAppId(dto.getAppId());
        res.setAliPayUserId(dto.getAlipayUserId());
        res.setOutNo(dto.getOutNo());
        res.setAvatar(dto.getAvatar());
        res.setNickname(dto.getNickname());
        res.setUnionId(dto.getUnionId());

        if (!CollectionUtils.isEmpty(dto.getFields())) {
            List<ExtendFieldValue> fieldValues = dto.getFields().stream().map(this::convert).collect(Collectors.toList());
            res.setFieldValList(JSONObject.toJSONString(fieldValues));
        }

        if (!CollectionUtils.isEmpty(dto.getLabels())) {
            List<LabelValueId> labelValueIds = dto.getLabels().stream().map(this::convert).collect(Collectors.toList());
            res.setLabels(JSONObject.toJSONString(labelValueIds));
        }
        return res;
    }

    private LabelValueId convert(MemberLabelParam dto) {
        LabelValueId res = new LabelValueId();
        res.setId(dto.getId());
        res.setLabelId(dto.getLabelId());
        return res;
    }

    private ExtendFieldValue convert(MemberFieldParam dto) {
        ExtendFieldValue res = new ExtendFieldValue();
        res.setFieldId(dto.getId());
        res.setFieldType(dto.getType());
        res.setFieldName(dto.getName());
        if (ExtendFieldTypeEnum.DATETIME.matches(dto.getType()) && StringUtils.hasText(dto.getValue())) {
            ServiceResult<Date> result = DateUtil.parse(dto.getValue());
            if (result.isSuccess()) {
                res.setDateValue(result.getData());
            }
        } else {
            res.setFieldValue(dto.getValue());
        }
        return res;
    }

    private MemberDTO convert(MemberDO entity) {
        MemberDTO res = new MemberDTO();
        res.setId(entity.getId());
        res.setOrgId(entity.getOrgId());
        res.setSysId(entity.getSysId());
        res.setSource(entity.getSource());
        res.setAvatar(entity.getAvatar());
        res.setName(entity.getName());
        res.setNickname(entity.getNickname());
        res.setGender(entity.getGender());
        String addressStr = entity.getAddress();
        if (StrUtil.isNotBlank(addressStr)) {
            List<String> addressList = JSONArray.parseArray(addressStr, String.class);
            if (CollUtil.isNotEmpty(addressList)) {
                res.setAddress(addressList);
            } else {
                res.setAddress(new ArrayList<>());
            }
        } else {
            res.setAddress(new ArrayList<>());
        }
        res.setMobile(entity.getPhone());
        res.setEmail(entity.getEmail());
        res.setBirthday(entity.getBirthday());
        res.setAge(entity.getAge());
        // res.setRegion(entity.get());
        res.setStore(entity.getStoreId());
        res.setStatus(entity.getStatus());
        res.setContent(entity.getContent());
        res.setEmployeeId(entity.getEmployeeId());
        res.setEmployeeStatus(entity.getEmployeeStatus());
        res.setDepartment(entity.getDepartment());
        res.setPosition(entity.getPosition());
        res.setInstituteId(entity.getInstituteId());
        res.setInstituteName(entity.getInstituteName());
        res.setUserId(entity.getUserId());
        res.setMemberNo(entity.getMemberNo());
        res.setMemberType(entity.getMemberType());
        res.setOpenId(entity.getOpenId());
        res.setAppId(entity.getAppId());
        res.setUnionId(entity.getUnionId());
        res.setAlipayUserId(entity.getAliPayUserId());
        res.setCreateTime(entity.getCreateTime());
        res.setUpdateTime(entity.getUpdateTime());
        res.setOutNo(entity.getOutNo());
        String labels = entity.getLabels();
        if (StrUtil.isNotBlank(labels)) {
            List<LabelValueId> labelValueIds = JSONArray.parseArray(labels, LabelValueId.class);
            if (CollUtil.isNotEmpty(labelValueIds)) {
                List<LabelValueIdDTO> labelValueDTOS = labelValueIds.stream().map(this::convert).collect(Collectors.toList());
                res.setLabelValueIds(labelValueDTOS);
            }
        }
        String fieldValList = entity.getFieldValList();
        if (StrUtil.isNotBlank(fieldValList)) {
            List<ExtendFieldValue> extendFieldValues = JSONArray.parseArray(fieldValList, ExtendFieldValue.class);
            if (CollUtil.isNotEmpty(extendFieldValues)) {
                List<ExtendFieldDTO> extendFieldDTOS = extendFieldValues.stream().map(this::convert).collect(Collectors.toList());
                res.setFieldValList(extendFieldDTOS);
            }
        }
        return res;
    }

    private LabelValueIdDTO convert(LabelValueId entity) {
        LabelValueIdDTO res = new LabelValueIdDTO();
        res.setId(entity.getId());
        res.setLabelId(entity.getLabelId());
        return res;
    }

    private ExtendFieldDTO convert(ExtendFieldValue entity) {
        ExtendFieldDTO res = new ExtendFieldDTO();
        res.setId(entity.getFieldId());
        res.setType(entity.getFieldType());
        res.setName(entity.getFieldName());
        if (entity.getFieldValue() != null) {
            if (ExtendFieldTypeEnum.DATETIME.matches(entity.getFieldType()) && entity.getFieldValue() instanceof Date) {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
                res.setValue(simpleDateFormat.format(entity.getFieldValue()));
            } else {
                res.setValue(entity.getFieldValue().toString());
            }
        }
        return res;
    }

    /**
     * 简单校验标签参数
     *
     * @param param 更新或者创建标签标签参数
     * @return param-errorMsg Map
     */
    private Map<UpsertLabelParam, String> batchValidateParam(BatchUpsertLabelsParam param) {
        Map<UpsertLabelParam, String> result = new HashMap<>();
        // 简单校验
        // 个人联系人基本字段
        Set<String> fixedFieldNames = FieldDisplayNameConstant.getMemberFixFieldNames(MemberTypeEnum.INDIVIDUAL);
        // 计算数据字段
        Set<String> computeFieldNames = FieldDisplayNameConstant.getComputeFixFieldNames();
        Set<String> labelNameSet = new HashSet<>(param.getUpsertLabelParamList().size());
        for (UpsertLabelParam upsertLabelParam : param.getUpsertLabelParamList()) {
            // Validator校验
            Set<ConstraintViolation<Object>> violations = GRpcUtil.validateParam(upsertLabelParam);
            if (!CollectionUtils.isEmpty(violations)) {
                ConstraintViolation<Object> violation = violations.iterator().next();
                result.put(upsertLabelParam, violation.getMessage());
                continue;
            }

            // 标签组名称限制长度
            if (StringUtils.hasText(upsertLabelParam.getLabelGroupName())
                    && upsertLabelParam.getLabelGroupName().length() > Constants.MAX_LABEL_GROUP_NAME_LENGTH) {
                upsertLabelParam.setLabelGroupName(upsertLabelParam.getLabelGroupName().substring(0, Constants.MAX_LABEL_GROUP_NAME_LENGTH));
            }

            // 标签名称限制长度
            if (StringUtils.hasText(upsertLabelParam.getLabelName())
                    && upsertLabelParam.getLabelName().length() > Constants.MAX_LABEL_NAME_LENGTH) {
                upsertLabelParam.setLabelName(upsertLabelParam.getLabelName().substring(0, Constants.MAX_LABEL_NAME_LENGTH));
            }

            // 未命名分组默认名称
            if (!StringUtils.hasText(upsertLabelParam.getLabelGroupName())) {
                upsertLabelParam.setLabelGroupName(Constants.DEFAULT_LABEL_GROUP_NAME);
            }

            // 校验标签名称是否重复
            if (!labelNameSet.add(upsertLabelParam.getLabelName())) {
                result.put(upsertLabelParam, "标签名称重复");
                continue;
            }

            // 校验标签中的标签值名称是否重复
            Set<String> valueSet = new HashSet<>(upsertLabelParam.getLabelValueNames().size());
            for (String value : upsertLabelParam.getLabelValueNames()) {
                if (!valueSet.add(value)) {
                    break;
                }
            }
            if (valueSet.size() < upsertLabelParam.getLabelValueNames().size()) {
                result.put(upsertLabelParam, "标签值重复");
                continue;
            }

            // 标签值数量
            if (upsertLabelParam.getLabelValueNames().size() > Constants.MAX_LABEL_VALUE_SIZE) {
                result.put(upsertLabelParam, String.format("标签值数量不能超过%s个", Constants.MAX_LABEL_VALUE_SIZE));
                break;
            }

            // 比较联系人固定基本字段
            if (fixedFieldNames.contains(upsertLabelParam.getLabelName())) {
                result.put(upsertLabelParam, "标签名与联系人基本字段重复");
                continue;
            }

            // 比较计算数值字段
            if (computeFieldNames.contains(upsertLabelParam.getLabelName())) {
                result.put(upsertLabelParam, "标签名与计算数值字段重复");
            }
        }
        return result;
    }
}
